﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Kinect;
using Microsoft.Speech.Recognition;
using System.IO;
using Microsoft.Speech.AudioFormat;
using System.Reflection;
using System.Xml;

namespace KinectGame
{
    public class VoiceController
    {
        private static VoiceController _instance;
        private KinectSensor sensor;
        private SpeechRecognitionEngine speechEngine;
        private GameContext gameContext;
        private KinectBaseForm activeWindow;
        private IList<string> alphabet;
        private int horizontalItem;
        private int verticalItem;

        public static VoiceController Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new VoiceController();
                return _instance;
            }
        }

        private VoiceController()
        { 
        }

        public void StartSensor(GameContext gameContext)
        {
            this.gameContext = gameContext; 
            foreach (var potentialSensor in KinectSensor.KinectSensors)
            {
                if (potentialSensor.Status == KinectStatus.Connected)
                {
                    this.sensor = potentialSensor;
                    break;
                }
            }

            if (null == this.sensor)
            {
                this.gameContext.ApplicationClose();
                return; 
            }
            try
            {
                this.sensor.Start();
            }
            catch (IOException)
            {
                this.gameContext.ApplicationClose();
                return; 
            }

            RecognizerInfo ri = GetKinectRecognizer();

            if (null == ri)
            {
                this.gameContext.ApplicationClose();
                return;
            }

            this.alphabet = new List<string>();
            CreateAlphabet(); 
        }

        public void SetActiveWindow(KinectBaseForm activeWindow, int number)
        {
            this.speechEngine = new SpeechRecognitionEngine();

            Console.WriteLine("sensor started");
            
            this.activeWindow = activeWindow;
            switch (number)
            {
                case 0:
                    using (var memoryStream = new MemoryStream(Encoding.ASCII.GetBytes(Properties.Resources.MenuGrammar)))
                    {
                        var g = new Grammar(memoryStream);
                        speechEngine.LoadGrammar(g);
                    }
                    speechEngine.SpeechRecognized += SpeechRecognized;
                    break; 
                case 1:
                    using (var memoryStream = new MemoryStream(Encoding.ASCII.GetBytes(Properties.Resources.GameGrammar)))
                    {
                        var g = new Grammar(memoryStream);
                        speechEngine.LoadGrammar(g);
                    }
                    speechEngine.SpeechRecognized += SpeechRecognizedGame;
                    this.horizontalItem = this.activeWindow.SetHorizontalItemNumber();
                    this.verticalItem = this.activeWindow.SetVerticalItemNumber();
                    break; 
                case 2:
                    using (var memoryStream = new MemoryStream(Encoding.ASCII.GetBytes(Properties.Resources.DefaultGrammar)))
                    {
                        var g = new Grammar(memoryStream);
                        speechEngine.LoadGrammar(g);
                    }
                    speechEngine.SpeechRecognized += SpeechRecognizedDefault;
                    break;
                case 3:
                    using (var memoryStream = new MemoryStream(Encoding.ASCII.GetBytes(Properties.Resources.HallOfFameGrammar)))
                    {
                        var g = new Grammar(memoryStream);
                        speechEngine.LoadGrammar(g);
                    }
                    speechEngine.SpeechRecognized += SpeechRecognizedHallOfFame;
                    break; 
            }
            speechEngine.SetInputToAudioStream(
                sensor.AudioSource.Start(), new SpeechAudioFormatInfo(EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null));
            speechEngine.RecognizeAsync(RecognizeMode.Multiple);
        }

        private static RecognizerInfo GetKinectRecognizer()
        {
            foreach (RecognizerInfo recognizer in SpeechRecognitionEngine.InstalledRecognizers())
            {
                string value;
                recognizer.AdditionalInfo.TryGetValue("Kinect", out value);
                if ("True".Equals(value, StringComparison.OrdinalIgnoreCase) && "en-US".Equals(recognizer.Culture.Name, StringComparison.OrdinalIgnoreCase))
                {
                    return recognizer;
                }
            }
            return null;
        }

        private void SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            Console.WriteLine("speaking"); 
            const double ConfidenceThreshold = 0.3;

            if (e.Result.Confidence >= ConfidenceThreshold)
            {
                Console.WriteLine("text recognised");
                switch (e.Result.Semantics.Value.ToString())
                {
                    case "START":
                        this.activeWindow.Click(0, 0);
                        Console.WriteLine("start game"); 
                        break;
                    case "QUIT":
                        this.activeWindow.Click(0, 3);
                        Console.WriteLine("quit game"); 
                        break;

                    case "FAME":
                        this.activeWindow.Click(0, 1);
                        Console.WriteLine("hall of fame"); 
                        break;

                    case "CREDITS":
                        this.activeWindow.Click(0, 2);
                        Console.WriteLine("credits"); 
                        break;
                }
            }
        }

        private void SpeechRecognizedHallOfFame(object sender, SpeechRecognizedEventArgs e)
        {
            Console.WriteLine("speaking");
            const double ConfidenceThreshold = 0.3;

            if (e.Result.Confidence >= ConfidenceThreshold)
            {
                Console.WriteLine("text recognised");
                switch (e.Result.Semantics.Value.ToString())
                {
                    case "LEFT":
                        this.activeWindow.Click(0, 0);
                        Console.WriteLine("left");
                        break;
                    case "MENU":
                        this.activeWindow.Click(1, 0);
                        Console.WriteLine("menu");
                        break;

                    case "RIGHT":
                        this.activeWindow.Click(2, 0);
                        Console.WriteLine("right");
                        break;
                }
            }
        }

        private void SpeechRecognizedGame(object sender, SpeechRecognizedEventArgs e)
        {
            Console.WriteLine("speaking");
            const double ConfidenceThreshold = 0.3;

            if (e.Result.Confidence >= ConfidenceThreshold)
            {
                if (e.Result.Semantics.Value.ToString().Equals("MENU"))
                {
                    this.activeWindow.ApplicationSpecificAction();
                    return; 
                }
                String[] strings = e.Result.Semantics.Value.ToString().Split(' ');
                int verticalIndex = this.alphabet.IndexOf(strings[0]);
                int horizontalIndex = Int32.Parse(strings[1]);
                Console.WriteLine(this.verticalItem + " " + this.horizontalItem);
                if (verticalIndex >= this.verticalItem) return;
                if (horizontalIndex > this.horizontalItem) return; 
                this.activeWindow.Click(horizontalIndex-1, verticalIndex); 
                Console.WriteLine("text recognised");
                Console.WriteLine(e.Result.Semantics.Value.ToString()); 
            }
        }

        private void SpeechRecognizedDefault(object sender, SpeechRecognizedEventArgs e)
        {
            Console.WriteLine("speaking");
            const double ConfidenceThreshold = 0.3;

            if (e.Result.Confidence >= ConfidenceThreshold)
            {
                Console.WriteLine("text recognised");
                Console.WriteLine(e.Result.Semantics.Value.ToString());
                this.activeWindow.Click(0, 0);
            }
        }

        private void CreateAlphabet()
        {
            this.alphabet.Add("A");
            this.alphabet.Add("B");
            this.alphabet.Add("C");
            this.alphabet.Add("D");
            this.alphabet.Add("E");
            this.alphabet.Add("F");
            this.alphabet.Add("G");
            this.alphabet.Add("H");
            this.alphabet.Add("I");
            this.alphabet.Add("J");
            this.alphabet.Add("K");
            this.alphabet.Add("L");
            this.alphabet.Add("M");
            this.alphabet.Add("N");
            this.alphabet.Add("O");
            this.alphabet.Add("P");
            this.alphabet.Add("R");
            this.alphabet.Add("S");
            this.alphabet.Add("T");
            this.alphabet.Add("U");
            this.alphabet.Add("V");
            this.alphabet.Add("W");
            this.alphabet.Add("X");
            this.alphabet.Add("Y");
            this.alphabet.Add("Z");
        }
    }
}
